#pragma once

#include <vex/iterable/range_base.h>
#include <vex/iterable/range_state.h>
#include <vex/iterable/adapter/adapter_access.h>
#include <cstddef>

namespace vex { namespace iterable { namespace adapter {
    template< 
        class TAdapter, 
        class TCategory = typename range_traits<TAdapter>::category, 
        class TRef = typename range_traits<TAdapter>::reference
    >
    class adapter_default;

    template<class TAdapter, class TRef>
    class adapter_default<TAdapter, trivial_range_tag, TRef> 
        : public range_marker 
    {
    protected:
        ~adapter_default() {}
    public:
        bool move_front()
        {
            return adapter_access::get_adaptee_ref<TAdapter>(*this)
                .move_front();
        }
    };

    template<class TAdapter, class TRef>
    class adapter_default<TAdapter, output_range_tag, TRef> 
        : public adapter_default<TAdapter, trivial_range_tag, TRef> 
    {
    protected:
        ~adapter_default() {}
    public:
        template<class TValue>
        bool put(TValue && p_value)
        {
            return adapter_access::get_adaptee_ref<TAdapter>(*this)
                .put(std::forward<TValue>(p_value));
        }
    };

    template<class TAdapter, class TRef>
    class adapter_default<TAdapter, one_pass_range_tag, TRef> 
        : public adapter_default<TAdapter, trivial_range_tag, TRef> 
    {
    protected:
        ~adapter_default() {}
    public:
        TRef front() const
        {
            return adapter_access::get_adaptee_ref<TAdapter>(*this)
                .front();
        }
    };

    template<class TAdapter, class TRef>
    class adapter_default<TAdapter, forward_range_tag, TRef> 
        : public adapter_default<TAdapter, one_pass_range_tag, TRef> 
    {
    protected:
        ~adapter_default() {}
    public:
        TAdapter save() const
        {
            return adapter_access::construct_from_saved<TAdapter>(*this);
        }
    };

    template<class TAdapter, class TRef>
    class adapter_default<TAdapter, bidirectional_range_tag, TRef> 
        : public adapter_default<TAdapter, forward_range_tag, TRef> 
    {
    protected:
        ~adapter_default() {}
    public:
        bool move_back()
        {
            return adapter_access::get_adaptee_ref<TAdapter>(*this)
                .move_back();
        }

        TRef back() const
        {
            return adapter_access::get_adaptee_ref<TAdapter>(*this)
                .back();
        }
    };

    template<class TAdapter, class TRef>
    class adapter_default<TAdapter, random_access_range_tag, TRef> 
        : public adapter_default<TAdapter, bidirectional_range_tag, TRef> 
    {
    protected:
        ~adapter_default() {}
    public:
        bool move_front_of(std::size_t p_diff)
        {
            return adapter_access::get_adaptee_ref<TAdapter>(*this)
                .move_front_of(p_diff);
        }

        bool move_back_of(std::size_t p_diff)
        {
            return adapter_access::get_adaptee_ref<TAdapter>(*this)
                .move_back_of(p_diff);
        }

        TRef at(std::size_t p_index) const
        {
            return adapter_access::get_adaptee_ref<TAdapter>(*this)
                .at(p_index);
        }

        std::size_t size() const
        {
            return adapter_access::get_adaptee_ref<TAdapter>(*this)
                .size();
        }
    };

}}}